/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

 /***************************************************************************
 * Name: service_provider.cpp
 *
 * Purpose: service provider
 *
 * Developer:
 *   wen.gu , 2021-08-05
 *
 * TODO:
 *
 ***************************************************************************/

 /******************************************************************************
 **    INCLUDES
 ******************************************************************************/
#include "icpp/com/service_provider.h"

#include <atomic>

#include "icpp/com/endpoint.h"
#include "icpp/core/plugin_manager.h"
#include "icpp/com/message_broker_simple.h"
#include "icpp/com/registration_center_client.h"

#define LOG_TAG "SrvP"
#include "icpp/core/log.h"
namespace icpp
{
namespace com
{
/******************************************************************************
 **    MACROS
 ******************************************************************************/
#define PRE_CHECK() (is_initialized_ || (msg_broker_ == nullptr))

#define PRE_CHECK_AND_RETURN() do{if (PRE_CHECK()) { return IcppErrc::InvalidStatus;}}while(0)
/******************************************************************************
 **    VARIABLE DEFINITIONS
 ******************************************************************************/

/******************************************************************************
 **    FUNCTION DEFINITIONS
 ******************************************************************************/

ServiceProvider::ServiceProvider()
    :msg_broker_ptr_(std::make_shared<MessageBrokerSimple>(MessageBrokerRole::kService))
{
    /** todo something */
}

ServiceProvider::~ServiceProvider()
{
    /** todo something */
}


core::IcppErrc ServiceProvider::initialize(const ServiceProviderInitializeParam& param)
{
    if (param.service_name.empty())
    {
        return IcppErrc::BadParameter;
    }

    UrlList srv_urls = param.urls;
    if (srv_urls.empty())
    {
        Response<UrlList> rep_urls = RegistrationCenterClient::getInstance().allocUrls(param.service_name);

        Result<UrlList> res_urls = rep_urls.getResult(); /** todo, refineme, maybe dead block */

        if (res_urls.hasValue() == false)
        {
            LOGE("alloc urls for service(%s) from registration center failed(%s)\n", param.service_name.c_str(), core::ErrorStr(res_urls.error()));
            return res_urls.error();
        }

        srv_urls = res_urls.value();
    }

    if (srv_urls.empty())
    {
        return IcppErrc::BadParameter;
    }

    MessageBrokerInitializeParam broker_param;
    broker_param.broker_name = "srv: " + param.service_name;
    broker_param.protocol_version = COM_PROTOCOL_VERSION;
    broker_param.interface_version = param.interface_version;
    broker_param.connection_state_handler = [this](EndpointId id, bool is_connected)
    {
        /** todo something */
    };  

    IcppErrc ret = msg_broker_ptr_->initialize(srv_urls, broker_param);

    if (IcppErrc::OK == ret)
    {
        service_info_.service_name = param.service_name;
        service_info_.service_id = param.service_id;
        service_info_.urls = srv_urls;
        service_info_.interface_version = param.interface_version;
    }
    else
    {
        LOGE("initialize message broker as 'service' failed(%s)\n", core::ErrorStr(ret));
    }

    return ret;
}

core::IcppErrc ServiceProvider::offerService()
{
    IcppErrc ret = msg_broker_ptr_->start();   

    if (IcppErrc::OK != ret)
    {
        msg_broker_ptr_->uninitialize();
        return ret;
    }

    /** todo, if register failed?? */
    Response<void> res = RegistrationCenterClient::getInstance().registerService(service_info_);
    Result<void> res_val = res.getResult();

    return res_val.hasValue() ? IcppErrc::OK : res_val.error();
}

core::IcppErrc ServiceProvider::stopService()
{
    return msg_broker_ptr_->stop();
}


ServiceProvider::IcppErrc ServiceProvider::sendMessage(MessagePtr msg_ptr)
{
    return msg_broker_ptr_->sendMessage(msg_ptr);
}


/** the type only could be: notification and property notify */
ServiceProvider::IcppErrc ServiceProvider::notify(MessageType type, MessageId message_id, PayloadPtr payload_ptr)
{
    MessagePtr msg = msg_broker_ptr_->newMessage(type, message_id, payload_ptr);

    return sendMessage(msg);
}

ServiceProvider::IcppErrc ServiceProvider::registerMessageHandler(MessageType type, MessageId method_id, MessageHandler method)
{
    return msg_broker_ptr_->registerMessageHandler(type, method_id, method);
}

/** for message: create, serialize, deserialize */
/** MessageId:  include: re*/
ServiceProvider::MessagePtr ServiceProvider::newMessage(MessageType type, MessageId message_id, PayloadPtr payload_ptr)
{
    MessagePtr msg_ptr = msg_broker_ptr_->newMessage(type, message_id, payload_ptr);

    if (msg_ptr)
    {
        msg_ptr->set_service_id(service_info_.service_id);
    }

    return msg_ptr;
}

//ServiceProvider::MessagePtr newResponseMessage(MessageId message_id, ClientId client_id, SessionId session_id, PayloadPtr reply_ptr = nullptr);

ServiceProvider::SerializerPtr ServiceProvider::newSerializer()
{
    return msg_broker_ptr_->newSerializer();
}

ServiceProvider::DeserializerPtr ServiceProvider::newDeserializer(PayloadPtr payload)
{
    return msg_broker_ptr_->newDeserializer(payload);
}




} /** namespace com */
} /** namespace icpp */
